//
//  NSObject+KHDBTable.m
//  AMapDemo-iOS
//
//  Created by apang on 2017/12/20.
//  Copyright © 2017年 men.apang.AMapDemo-iOS. All rights reserved.
//
//  https://www.sqlite.org/datatype3.html sqlite DataTypes

//  https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html Apple EncodeTypes

//  https://github.com/ziminji/objective-c-sql-query-builder/wiki/Mapping-sqlite-and-objective-c-data-types

/**
 c                  A char
 i                  An int
 s                  A short
 l                  "A long is treated as a 32-bit quantity on 64-bit programs."
 q                  A long long
 C                  An unsigned char
 I                  An unsigned int
 S                  An unsigned short
 L                  An unsigned long
 Q                  An unsigned long long
 f                  A float
 d                  A double
 B                  A C++ bool or a C99 _Bool
 v                  A void
 *                  A character string (char *)
 @                  An object (whether statically typed or typed id)
 #                  A class object (Class)
 :                  A method selector (SEL)
 [array type]       An array
 {name=type...}     A structure
 (name=type...)     A union
 bnum               A bit field of num bits
 ^type              A pointer to type
 ?                  An unknown type (among other things, this code is used for function pointers)
 */

/**
 
 iOS sqlite3.h
 
 #define SQLITE_INTEGER  1
 #define SQLITE_FLOAT    2
 #define SQLITE_BLOB     4
 #define SQLITE_NULL     5
 #ifdef SQLITE_TEXT
 # undef SQLITE_TEXT
 #else
 # define SQLITE_TEXT     3
 #endif
 #define SQLITE3_TEXT     3
 */

#define SQLTEXT     @"TEXT"
#define SQLINTEGER  @"INTEGER"
#define SQLFLOAT    @"REAL" //亲和类型
#define SQLBLOB     @"BLOB"
#define SQLNULL     @"NULL"

#import "NSObject+KHDBTable.h"
#import <objc/runtime.h>
#import <objc/message.h>
#import <sqlite3.h>
#import <FMDB/FMDB.h>

static NSString * primaryKey = @"sort";
static NSString * TableNameKey = @"tableName";

@interface KHClassPropertyInfo :NSObject
@property (nonatomic, assign, readonly) objc_property_t property_t;
@property (nonatomic, copy, readonly) NSString * name;
@property (nonatomic, assign, readonly) const char *type;
@property (nonatomic, copy, readonly) NSString * sql_type;
@property (nonatomic, assign, readonly) SEL getter;
@property (nonatomic, assign, readonly) SEL setter;

- (instancetype)initWithProperty:(objc_property_t)property_t;
@end

@implementation KHClassPropertyInfo
- (instancetype)initWithProperty:(objc_property_t)property_t {
    self = [super init];
    if (self) {
        _property_t = property_t;
        const char * name = property_getName(property_t);
        if (name) {
            _name = [NSString stringWithUTF8String:name];
        }
        unsigned int count;
        objc_property_attribute_t * property_attribute_t_arr = property_copyAttributeList(_property_t, &count);
        for (unsigned int i=0; i<count; i++) {
            objc_property_attribute_t property_attribute_t = property_attribute_t_arr[i];
            const char * value = property_attribute_t.value;
            switch (property_attribute_t.name[0]) {
                case 'T':{
                    [self setType:value];
                } break;
                case 'G':{
                    if (value) {
                        _getter = NSSelectorFromString([NSString stringWithUTF8String:value]);
                    }
                } break;
                case 'S': {
                    if (value) {
                        _setter = NSSelectorFromString([NSString stringWithUTF8String:value]);
                    }
                } break;
                    
                default:
                    break;
            }
        }
        if (_name.length) {
            if (!_getter) {
                _getter = NSSelectorFromString(_name);
            }
            if (!_setter) {
                _setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", [_name substringToIndex:1].uppercaseString, [_name substringFromIndex:1]]);
            }
        }
    }
    return self;
}
- (void)setType:(const char *)type {
    _type = type;
    /*
     c                  A char
     i                  An int
     s                  A short
     l                  "A long is treated as a 32-bit quantity on 64-bit programs."
     q                  A long long
     C                  An unsigned char
     I                  An unsigned int
     S                  An unsigned short
     L                  An unsigned long
     Q                  An unsigned long long
     f                  A float
     d                  A double
     B                  A C++ bool or a C99 _Bool
     @                  An object
     */
    switch (type[0]) {
        case 'c':{//A char
            _sql_type = SQLTEXT;
        } break;
        case 'i':{//An int
            _sql_type = SQLINTEGER;
        } break;
        case 's':{//A short
            _sql_type = SQLINTEGER;
        } break;
        case 'l':{//"A long is treated as a 32-bit quantity on 64-bit programs."
            _sql_type = SQLINTEGER;
        } break;
        case 'q':{//A long long
            _sql_type = SQLINTEGER;
        } break;
        case 'C':{//An unsigned char
            _sql_type = SQLTEXT;
        } break;
        case 'I':{//An unsigned int
            _sql_type = SQLINTEGER;
        } break;
        case 'S':{//An unsigned short
            _sql_type = SQLINTEGER;
        } break;
        case 'L':{//An unsigned long
            _sql_type = SQLINTEGER;
        } break;
        case 'Q':{//An unsigned long long
            _sql_type = SQLINTEGER;
        } break;
        case 'f':{//A float
            _sql_type = SQLFLOAT;
        } break;
        case 'd':{//A double
            _sql_type = SQLFLOAT;
        } break;
        case 'B':{//A C++ bool or a C99 _Bool
            _sql_type = SQLINTEGER;
        } break;
        case '@':{//An object
            _sql_type = SQLTEXT;
            NSScanner *scanner = [NSScanner scannerWithString:[NSString stringWithUTF8String:type]];
            NSString *clsName = nil;
            if ([scanner scanString:@"@\"" intoString:NULL]) {
                if ([scanner scanUpToCharactersFromSet: [NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) {
                    if (clsName.length) {
                        Class cls = objc_getClass(clsName.UTF8String);
                        if ([cls isEqual:[NSData class]]) {
                            _sql_type = SQLBLOB;
                        }
                    }
                }
            }
            
        } break;
            
        default:
            _sql_type = SQLTEXT;
            break;
    }
}
@end

@interface KHClassInfo :NSObject
@property (nonatomic, assign, readonly) Class cls;
@property (nonatomic, copy, readonly) NSString * name;
@property (nullable, nonatomic, strong, readonly) NSMutableArray * properties;
+ (nullable instancetype)classInfoWithClass:(Class)cls needUpdate:(BOOL)update;
+ (nullable instancetype)classInfoWithClassName:(NSString *)className;
@end
@implementation KHClassInfo

- (void)_update {
    Class cls = self.cls;
    _properties = [NSMutableArray array];
    unsigned int propertyCount = 0;
    objc_property_t *properties = class_copyPropertyList(cls, &propertyCount);
    if (properties) {
        NSArray * ignorePropertyList = @[];
        if ([cls respondsToSelector:@selector(ignorePropertyNams)]) {
            ignorePropertyList = [(id<KHDBTable>)cls ignorePropertyNams];
        }
        for (unsigned int i = 0; i < propertyCount; i++) {
            objc_property_t tmp_property_t = properties[i];
            //FIXME:请注意数组的包含函数是否可靠
            if (ignorePropertyList.count>0 && [ignorePropertyList containsObject:[NSString stringWithUTF8String:property_getName(tmp_property_t)]]) {
                continue;
            }
            KHClassPropertyInfo *info = [[KHClassPropertyInfo alloc] initWithProperty:tmp_property_t];
            if (info.name) {
                [_properties addObject:info];
            }
        }
        free(properties);
    }
}

- (instancetype)initWithClass:(Class)cls {
    if (!cls) return nil;
    self = [super init];
    _cls = cls;
    _name = NSStringFromClass(cls);
    [self _update];
    return self;
}

+ (instancetype)classInfoWithClassName:(NSString *)className {
    return [self classInfoWithClass:NSClassFromString(className) needUpdate:NO];
}

+ (instancetype)classInfoWithClass:(Class)cls needUpdate:(BOOL)update{
    if (!cls) return nil;
    static CFMutableDictionaryRef classCache;
    static dispatch_once_t onceToken;
    static dispatch_semaphore_t lock;
    dispatch_once(&onceToken, ^{
        classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
        lock = dispatch_semaphore_create(1);
    });
    dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
    KHClassInfo *info = CFDictionaryGetValue(classCache, (__bridge const void *)(cls));
    if (info && update) {
        info = nil;
    }
    dispatch_semaphore_signal(lock);
    if (!info) {
        info = [[KHClassInfo alloc] initWithClass:cls];
        if (info) {
            dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
            CFDictionarySetValue(classCache, (__bridge const void *)(cls), (__bridge const void *)(info));
            dispatch_semaphore_signal(lock);
        }
    }
    return info;
}
@end

static __inline__ __attribute__((always_inline)) void SetValueForProperty(__unsafe_unretained id table, __unsafe_unretained KHClassInfo * classInfo, __unsafe_unretained FMResultSet * resultSet){
    for (KHClassPropertyInfo *propertyInfo in classInfo.properties) {
        id value = [resultSet objectForColumn:propertyInfo.name];
        if ([value isKindOfClass:[NSNull class]]) {
            continue;
        }
        switch (propertyInfo.type[0]) {
            case 'c':{//A char
                ((void (*)(id, SEL, int8_t))(void *) objc_msgSend)(table, propertyInfo.setter, (int8_t)[value charValue]);
            } break;
            case 'i':{//An int
                ((void (*)(id, SEL, int32_t))(void *) objc_msgSend)(table, propertyInfo.setter, (int32_t)[value intValue]);
            } break;
            case 's':{//A short
                ((void (*)(id, SEL, int16_t))(void *) objc_msgSend)(table, propertyInfo.setter, (int16_t)[value shortValue]);
            } break;
            case 'l':{//"A long is treated as a 32-bit quantity on 64-bit programs."
                ((void (*)(id, SEL, int32_t))(void *) objc_msgSend)(table, propertyInfo.setter, (int32_t)[value intValue]);
            } break;
            case 'q':{//A long long
                ((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)(table,propertyInfo.setter, (uint64_t)[value longLongValue]);
            } break;
            case 'C':{//An unsigned char
                ((void (*)(id, SEL, uint8_t))(void *) objc_msgSend)(table, propertyInfo.setter, (uint8_t)[value unsignedCharValue]);
            } break;
            case 'I':{//An unsigned int
                ((void (*)(id, SEL, uint32_t))(void *) objc_msgSend)(table, propertyInfo.setter, (uint32_t)[value unsignedIntValue]);
            } break;
            case 'S':{//An unsigned short
                ((void (*)(id, SEL, uint16_t))(void *) objc_msgSend)(table, propertyInfo.setter, (uint16_t)[value unsignedShortValue]);
            } break;
            case 'L':{//An unsigned long
                ((void (*)(id, SEL, uint32_t))(void *) objc_msgSend)(table, propertyInfo.setter, (uint32_t)[value unsignedIntValue]);
            } break;
            case 'Q':{//An unsigned long long
                ((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)(table, propertyInfo.setter, (uint64_t)[value unsignedLongLongValue]);
            } break;
            case 'f':{//A float
                float f = [value floatValue];
                if (isnan(f) || isinf(f)) f = 0;
                ((void (*)(id, SEL, float))(void *) objc_msgSend)(table, propertyInfo.setter, f);
            } break;
            case 'd':{//A double
                double d = [value doubleValue];
                if (isnan(d) || isinf(d)) d = 0;
                ((void (*)(id, SEL, double))(void *) objc_msgSend)(table, propertyInfo.setter, d);
            } break;
            case 'B':{//A C++ bool or a C99 _Bool
                ((void (*)(id, SEL, bool))(void *) objc_msgSend)(table, propertyInfo.setter, [value boolValue]);
            } break;
            case '@':{//An object
                NSScanner *scanner = [NSScanner scannerWithString:[NSString stringWithUTF8String:propertyInfo.type]];
                NSString *clsName = nil;
                if ([scanner scanString:@"@\"" intoString:NULL]) {
                    if ([scanner scanUpToCharactersFromSet: [NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) {
                        if (clsName.length) {
                            Class cls = objc_getClass(clsName.UTF8String);
                            if ([cls isEqual:[NSData class]]) {
                                ((void (*)(id, SEL, NSData *))(void *) objc_msgSend)(table, propertyInfo.setter, [resultSet dataForColumn:propertyInfo.name]);
                            }else if ([cls isEqual:[NSString class]]){
                                ((void (*)(id, SEL, NSString *))(void *) objc_msgSend)(table, propertyInfo.setter, [resultSet stringForColumn:propertyInfo.name]);
                            }else if ([cls isEqual:[NSDate class]]){
                                ((void (*)(id, SEL, NSDate *))(void *) objc_msgSend)(table, propertyInfo.setter, [resultSet dateForColumn:propertyInfo.name]);
                            }else{
                                ((void (*)(id, SEL, id))(void *) objc_msgSend)(table, propertyInfo.setter, value);
                            }
                        }else {
                            ((void (*)(id, SEL, id))(void *) objc_msgSend)(table, propertyInfo.setter, value);
                        }
                    } else {
                        ((void (*)(id, SEL, id))(void *) objc_msgSend)(table, propertyInfo.setter, value);
                    }
                }else {
                    ((void (*)(id, SEL, id))(void *) objc_msgSend)(table, propertyInfo.setter, value);
                }
                
            } break;
                
            default:
                break;
        }
    }
}

static __inline__ __attribute__((always_inline)) id GetValueFromProperty(__unsafe_unretained id table, __unsafe_unretained KHClassPropertyInfo * propertyInfo){
    switch (propertyInfo.type[0]) {
        case 'c':{//A char
            return @(((char (*)(id, SEL))(void *) objc_msgSend)(table, propertyInfo.getter));
        } break;
        case 'i':{//An int
            return @(((int (*)(id, SEL))(void *) objc_msgSend)(table, propertyInfo.getter));
        } break;
        case 's':{//A short
            return @(((short (*)(id, SEL))(void *) objc_msgSend)(table, propertyInfo.getter));
        } break;
        case 'l':{//"A long is treated as a 32-bit quantity on 64-bit programs."
            return @(((long (*)(id, SEL))(void *) objc_msgSend)(table, propertyInfo.getter));
        } break;
        case 'q':{//A long long
            return @(((long long (*)(id, SEL))(void *) objc_msgSend)(table, propertyInfo.getter));
        } break;
        case 'C':{//An unsigned char
            return @(((unsigned char (*)(id, SEL))(void *) objc_msgSend)(table, propertyInfo.getter));
        } break;
        case 'I':{//An unsigned int
            return @(((unsigned int (*)(id, SEL))(void *) objc_msgSend)(table, propertyInfo.getter));
        } break;
        case 'S':{//An unsigned short
            return @(((unsigned short (*)(id, SEL))(void *) objc_msgSend)(table, propertyInfo.getter));
        } break;
        case 'L':{//An unsigned long
            return @(((unsigned long (*)(id, SEL))(void *) objc_msgSend)(table, propertyInfo.getter));
        } break;
        case 'Q':{//An unsigned long long
            return @(((unsigned long long (*)(id, SEL))(void *) objc_msgSend)(table, propertyInfo.getter));
        } break;
        case 'f':{//A float
            return @(((float (*)(id, SEL))(void *) objc_msgSend)(table, propertyInfo.getter));
        } break;
        case 'd':{//A double
            return @(((double (*)(id, SEL))(void *) objc_msgSend)(table, propertyInfo.getter));
        } break;
        case 'B':{//A C++ bool or a C99 _Bool
            return @(((BOOL (*)(id, SEL))(void *) objc_msgSend)(table, propertyInfo.getter));
        } break;
        case '@':{//anObject
            NSScanner *scanner = [NSScanner scannerWithString:[NSString stringWithUTF8String:propertyInfo.type]];
            NSString *clsName = nil;
            if ([scanner scanString:@"@\"" intoString:NULL]) {
                if ([scanner scanUpToCharactersFromSet: [NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) {
                    if (clsName.length) {
                        return ((id (*)(id, SEL))(void *) objc_msgSend)(table, propertyInfo.getter);
                    }
                }
            }
        } break;
            
        default:
            return nil;
            break;
    }
    return nil;
}

@implementation NSObject (KHDBTable)
- (void)setSort:(NSInteger)sort {
    objc_setAssociatedObject(self, &primaryKey, @(sort), OBJC_ASSOCIATION_ASSIGN);
}
- (NSInteger)sort {
    return [objc_getAssociatedObject(self, &primaryKey) integerValue];
}

- (void)setTableName:(NSString *)tableName {
    objc_setAssociatedObject(self, &TableNameKey, tableName, OBJC_ASSOCIATION_COPY);
}

- (NSString *)tableName {
    NSString * tmpName = [objc_getAssociatedObject(self, &TableNameKey) stringValue];
    return (tmpName.length>0)?tmpName:NSStringFromClass(self.class);
}

+ (NSString *)tableColumnAndTypes {
    NSMutableString* pars = [NSMutableString stringWithFormat:@"%@ INTEGER PRIMARY KEY,",primaryId];
    KHClassInfo * info = [KHClassInfo classInfoWithClass:self.class needUpdate:NO];
    for (int i = 0; i<info.properties.count; i++) {
        KHClassPropertyInfo * propertyInfo = info.properties[i];
        [pars appendFormat:@"%@ %@",propertyInfo.name,propertyInfo.sql_type];
        if(i+1 != info.properties.count)
        {
            [pars appendString:@","];
        }
    }
    return pars;
}

+ (BOOL)isExistsInTable:(NSString *)tableName {
    __block BOOL res = NO;
    __block NSString * dbTableName = (tableName.length>0)?tableName:NSStringFromClass(self.class);
    KHDBHelper *dbTools = [KHDBHelper shareInstance];
    [dbTools.dbQueue inDatabase:^(FMDatabase *db) {
        res = [db tableExists:dbTableName];
    }];
    return res;
}

+ (BOOL)createTable:(NSString *)tableName {
    __block BOOL res = NO;
    KHDBHelper * dbTools = [KHDBHelper shareInstance];
    __block NSString * dbTableName = (tableName.length>0)?tableName:NSStringFromClass(self.class);
    [dbTools.dbQueue inTransaction:^(FMDatabase * _Nonnull db, BOOL * _Nonnull rollback) {
        NSString *columeAndType = [self tableColumnAndTypes];
        NSString *sql = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@(%@);",dbTableName,columeAndType];
        if (![db executeUpdate:sql]) {
            *rollback = YES;
            res = YES;
            return;
        };

    }];
    return res;
}

+ (BOOL)_updateTable:(NSString *)tableName {
    __block BOOL res = NO;
    KHDBHelper * dbTools = [KHDBHelper shareInstance];
    __block NSString * dbTableName = (tableName.length>0)?tableName:NSStringFromClass(self.class);
    [dbTools.dbQueue inTransaction:^(FMDatabase * _Nonnull db, BOOL * _Nonnull rollback) {
        NSMutableArray *columns = [NSMutableArray array];
        FMResultSet *resultSet = [db getTableSchema:dbTableName];
        while ([resultSet next]) {
            NSString *column = [resultSet stringForColumn:@"name"];
            [columns addObject:column];
        }
        NSMutableArray * propertyNames = [NSMutableArray array];
        NSMutableDictionary * propertyDic = [NSMutableDictionary dictionary];
        KHClassInfo * info = [KHClassInfo classInfoWithClass:self.class needUpdate:YES];
        for (KHClassPropertyInfo * propertyInfo in info.properties) {
            [propertyNames addObject:propertyInfo.name];
            [propertyDic setObject:propertyInfo forKey:propertyInfo.name];
        }
        NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:@"NOT (SELF IN %@)",columns];
        //过滤数组
        NSArray *resultArray = [propertyNames filteredArrayUsingPredicate:filterPredicate];
        for (NSString * column in resultArray) {
            KHClassPropertyInfo * propertyInfo = (KHClassPropertyInfo *)[propertyDic objectForKey:column];
            NSString *fieldSql = [NSString stringWithFormat:@"%@ %@",column,propertyInfo.sql_type];
            NSString *sql = [NSString stringWithFormat:@"ALTER TABLE %@ ADD COLUMN %@ ",dbTableName,fieldSql];
            if (![db executeUpdate:sql]) {
                res = NO;
                *rollback = YES;
                return ;
            }
        }
    }];
    return res;
}

+ (BOOL)updateTable:(NSString *)tableName {
    if (![self isExistsInTable:tableName]) {
        if ([self createTable:tableName]) {
            return [self _updateTable:tableName];
        }
    }else{
        return [self _updateTable:tableName];
    }
    return NO;
}

+ (void)testClassInfo {
    unsigned int property_t_count;
    objc_property_t * property_t = class_copyPropertyList([self class], &property_t_count);
    for (int i=0; i<property_t_count; i++) {
        unsigned int property_attribute_t_count;
        objc_property_t tmp_property_t = property_t[i];
        objc_property_attribute_t * property_attribute_t = property_copyAttributeList(tmp_property_t, &property_attribute_t_count);
        //        NSString * property_name = [NSString stringWithUTF8String:property_getName(tmp_property_t)];
        NSLog(@"属性名:%s  属性的属性:%s",property_getName(tmp_property_t),property_getAttributes(tmp_property_t));
        //        NSLog(@"set%@%@:", [property_name substringToIndex:1].uppercaseString, [property_name substringFromIndex:1]);
        for (int j=0; j<property_attribute_t_count; j++) {
            objc_property_attribute_t tmp_property_attribute_t = property_attribute_t[j];
            NSLog(@"%c  %s\n",tmp_property_attribute_t.name[0],tmp_property_attribute_t.value);
        }
    }
}

- (BOOL)save {
    __block BOOL res = NO;
    if (self.sort<=0) {
        if (![self.class isExistsInTable:self.tableName]) {
            [self.class createTable:self.tableName];
        }
        NSMutableString *columnString = [NSMutableString string];
        NSMutableString *valueString = [NSMutableString string];
        NSMutableArray *values = [NSMutableArray  array];
        KHClassInfo *info = [KHClassInfo classInfoWithClass:self.class needUpdate:NO];
        for (int i=0; i<info.properties.count; i++) {
            KHClassPropertyInfo * propertyInfo = info.properties[i];
            id value = GetValueFromProperty(self, propertyInfo);
            [columnString appendFormat:@"%@%@",(i==0)?@"":@",",propertyInfo.name];
            [valueString appendFormat:@"%@?",(i==0)?@"":@","];
            [values addObject:(value)?:@""];
        }
        KHDBHelper *dbTools = [KHDBHelper shareInstance];
        __block BOOL res = NO;
        [dbTools.dbQueue inDatabase:^(FMDatabase *db) {
            NSString *sql = [NSString stringWithFormat:@"INSERT INTO %@(%@) VALUES (%@);", self.tableName, columnString, valueString];
            res = [db executeUpdate:sql withArgumentsInArray:values];
            self.sort = res?[NSNumber numberWithLongLong:db.lastInsertRowId].intValue:0;
//            NSLog(res?@"插入成功":@"插入失败");
        }];
        return res;
    }else {
        return [self update];
    }
    return res;
}
+ (BOOL)saveObjects:(NSArray *)objects {
    __block BOOL res = NO;
    for (NSObject *obj in objects) {
        [obj save];
    }
    return res;
}
- (BOOL)deleteObject {
    __block BOOL res = NO;
    return res;
}
//+ (BOOL)deleteObjects:(NSArray *)objects;
//+ (BOOL)deleteObjectsWithPredicate:(NSString *)predicate;
//+ (BOOL)deleteObjectsWithFormat:(NSString *)format, ...;
//- (BOOL)update;
//+ (BOOL)updateObjects:(NSArray *)objects;
//+ (BOOL)updateObjectsWithFormat:(NSString *)format, ...;
+ (NSArray *)inquiryAll:(NSString *)tableName {
    KHDBHelper *dbTools = [KHDBHelper shareInstance];
    NSMutableArray * result = [NSMutableArray array];
    __block NSString * tmpTableName = (tableName.length>0)?tableName:NSStringFromClass(self.class);
    [dbTools.dbQueue inTransaction:^(FMDatabase * _Nonnull db, BOOL * _Nonnull rollback) {
        NSString *sql = [NSString stringWithFormat:@"SELECT * FROM %@",tmpTableName];
        FMResultSet *resultSet = [db executeQuery:sql];
        KHClassInfo *info = [KHClassInfo classInfoWithClass:self.class needUpdate:NO];
        while ([resultSet next]) {
            id obj = [[info.cls alloc] init];
            [obj setSort:[resultSet intForColumn:primaryKey]];
            SetValueForProperty(obj, info, resultSet);
            [result addObject:obj];
            FMDBRelease(model);
        }
    }];
    return result;
}
//+ (NSArray *)inquiryWithPrimaryKey:(NSInteger)primaryKey;
//+ (NSArray *)inquiryWithPredicate:(NSString *)predicate;
//+ (NSArray *)inquiryWithFormat:(NSString *)format, ...;

@end
